#include <config.h>
#include <core.h>

#ifndef MSGD_HAVE_MSGHDR_MSG_CONTROL
#define MSGD_HAVE_MSGHDR_MSG_CONTROL 1
#endif

msgd_int_t msgd_write_channel(int fd , msgd_channel_t *ch, size_t size)
{
   
   struct iovec  vec[1]; 
   struct msghdr msg;
   msgd_int_t    n , err;
#ifdef MSGD_HAVE_MSGHDR_MSG_CONTROL 
   union {
      struct cmsghdr cm;
      char   space[CMSG_SPACE(sizeof(int))];
   } cmsg;
   if(ch->fd == -1){
      msg.msg_control = NULL;
      msg.msg_controllen = 0;
   }else{
      msg.msg_control = (caddr_t)&cmsg;
      msg.msg_controllen = sizeof(cmsg);
     
      cmsg.cm.cmsg_len = CMSG_LEN(sizeof(int));
      cmsg.cm.cmsg_level = SOL_SOCKET;
      cmsg.cm.cmsg_type  = SCM_RIGHTS;
      memcpy(CMSG_DATA(&cmsg.cm),&ch->fd,sizeof(int));
   }
#else
   if(ch->fd == -1){
     msg.msg_accrights = NULL;
     msg.msg_accrightslen = 0;
   }else{
     msg.msg_accrights =  (caddr_t)&ch->fd;
     msg.msg_accrightslen = sizeof(int);
   } 
#endif


  msg.msg_name = NULL;
  msg.msg_namelen = 0;


  vec[0].iov_base = (char*)ch;
  vec[0].iov_len = size;
  msg.msg_iov = vec;
  msg.msg_iovlen = 0;

  msg.msg_flags = 0;

  n = sendmsg(fd,&msg,0);
  if(n == -1){
    err = errno; 
    if(err == EAGAIN)
      return MSGD_AGAIN;
    ERROR("sendmsg (%d) error",fd);
    return MSGD_ERROR;
  }
  
  return MSGD_OK; 
}

msgd_int_t msgd_read_channel(int  fd , msgd_channel_t *ch, size_t size)
{
   msgd_int_t n , err;
   struct msghdr msg;
   struct iovec  vec[1];

#ifdef  MSGD_HAVE_MSGHDR_MSG_CONTROL
   union {
     struct cmsghdr cm;
     char   space[CMSG_SPACE(sizeof(int))];
   } cmsg;
#else
   int    fd;
#endif

   vec[0].iov_base = (char*)ch;
   vec[0].iov_len  = size;
   msg.msg_name = NULL;
   msg.msg_namelen = 0;

   msg.msg_iov = vec;
   msg.msg_iovlen = 1;
 
#ifdef  MSGD_HAVE_MSGHDR_MSG_CONTROL
   msg.msg_control = (caddr_t)&cmsg;
   msg.msg_controllen = sizeof(cmsg);
#else
   msg.msg_accrights = &fd;
   msg.msg_accrightslen = sizeof(int);
#endif

   n = recvmsg(fd,&msg,0);
   if(n == -1){
     err = errno;
     if(err == EAGAIN)  
       return MSGD_AGAIN;
     ERROR("recvmsg failed");
     return MSGD_ERROR;
   }

   if(n == 0){
     ERROR("recvmsg() returned zero");
     return MSGD_ERROR;
   }

   if((size_t)n < sizeof(msgd_channel_t)){
     ERROR("recvmsg() returned not enough data:%u expect:%d",n,sizeof(msgd_channel_t));
     return MSGD_ERROR;
   }

#ifdef  MSGD_HAVE_MSGHDR_MSG_CONTROL
   if(ch->command == MSGD_CMD_OPEN_CHANNEL){
     if(cmsg.cm.cmsg_len != CMSG_LEN(sizeof(int))){
       ERROR("recvmsg returned too small data");
       return MSGD_ERROR;
     } 
  
     if(cmsg.cm.cmsg_level != SOL_SOCKET){
       ERROR("recvmsg returned cmsg_level not SOL_SOCKET");
       return MSGD_ERROR;
     }
     
     if(cmsg.cm.cmsg_type != SCM_RIGHTS){
       ERROR("recvmsg returned cmsg_type not SCM_RIGHTS");
       return MSGD_ERROR;
     }

     memcpy(&ch->fd,CMSG_DATA(&cmsg.cm),sizeof(int));
   }
#else
   if(ch->command == MSGD_CMD_OPEN_CHANNEL){
     if(msg.accrightslen != sizeof(int)){
       ERROR("recvmsg returned not right data");
     }
     ch->fd = fd;
   }
#endif

   return n;
}

#if 0
msgd_int_t msgd_add_channel_event(int fd, msgd_int_t, event , msgd_event_handler_pt handler)
{

}
#endif

void       msgd_close_channel(int *fd)
{
   if(close(fd[0]) == -1)
     ERROR("close %d error",fd[0]);

   if(close(fd[1]) == -1)
     ERROR("close %d error",fd[1]);
}
